#ifndef STRING_TABLE_H

#define STRING_TABLE_H

#pragma warning( push )
#pragma warning( disable: 4555 ) // expression has no effect; expected expression with side-effect
#include <hash_map>
#pragma warning( pop )
#include <assert.h>
#include <algorithm>
#include "PlatformConfigGPSTXT.h"

//********** String Table
#pragma warning(push)
#pragma warning(disable:4996)

class StringHasher
{
public:
	static const size_t bucket_size = 4;
	static const size_t min_buckets = 8;

	size_t operator()(const char *key_value) const
	{
		size_t hash = 0;
		while (*key_value)
			hash = (hash >> 7) + (*key_value++ << 25);
		return hash;
	}

	bool operator()(const char *left, const char *right) const
	{
		return strcmp(left, right) < 0;
	}
};

typedef stdext::hash_map< const char *, const char *, StringHasher > StringHashMap;

class StringTable : public UANS::UserAllocated
{
public:

	StringTable(void)
	{
		mCaseSensitive = true;
	};

	~StringTable(void)
	{
		release();
	}

	void release(void)
	{
		for (StringHashMap::iterator i=mStrings.begin(); i!=mStrings.end(); ++i)
        {
            const char *string = (*i).second;
			GPSTXT_FREE( (void *)string );
        }
        mStrings.clear();
	}

	const char * Get(const char *str,bool &first)
	{
		str = str ? str : "";
		if ( !mCaseSensitive )
		{
			strncpy(mScratch,str,512);
			strlwr(mScratch);
			str = mScratch;
		}
		const char *ret;
		StringHashMap::iterator found = mStrings.find(str);
		if ( found == mStrings.end() )
		{
			gpstxt::GtU32 slen = (gpstxt::GtU32)strlen(str);
			char *string = (char *)GPSTXT_ALLOC(slen+1);
			strcpy(string,str);
			mStrings[string] = string;
			ret = string;
			first = true;
		}
		else
		{
			first = false;
			ret = (*found).second;
		}
#if 0 // additional debugging code
		found = mStrings.find(str);
		assert( found != mStrings.end() );
		assert( stricmp((*found).second,str) == 0 );
#endif
        return ret;
	};

	void setCaseSensitive(bool state)
	{
		mCaseSensitive = state;
	}

private:
	bool				mCaseSensitive;
    StringHashMap       mStrings;
	char				mScratch[512];
};

class StringTableInt
{
public:
	class StringUsage
	{
	public:

		inline bool operator < (const StringUsage& struct1) const
		{
			if ( struct1.mCount == mCount )
			{
				return strcmp(mString,struct1.mString) < 0;
			}
			return (struct1.mCount < mCount);
		}

		const char		*mString;
		gpstxt::GtU32	mValue;
		gpstxt::GtU32	mCount;
	};

	typedef std::vector< StringUsage > StringUsageVector;
	typedef stdext::hash_map< gpstxt::GtU32, const char * > IntStringMap;

	class IntEntry
	{
	public:
		IntEntry(gpstxt::GtU32 v)
		{
			mValue = v;
			mCount = 1;
		}
		IntEntry(void)
		{

		}
		gpstxt::GtU32	mValue;
		gpstxt::GtU32	mCount;
	};
	typedef stdext::hash_map< size_t, IntEntry > StringIntMap;

	StringTableInt(void)
	{
		mStringTable.setCaseSensitive(false);
	}

	bool Get(const char *str,gpstxt::GtU32 &v)
	{
		bool ret = false;
		bool first;
		str = mStringTable.Get(str,first);
		size_t index = (size_t)str;

		StringIntMap::iterator found = mStringInt.find(index);
		if ( found != mStringInt.end() )
		{
			v = (*found).second.mValue;
			(*found).second.mCount++;
			ret = true;
		}
		return ret;
	}

	void Add(const char *str,gpstxt::GtU32 v)
	{
		bool first;
		str = mStringTable.Get(str,first);
		size_t index = (size_t)str;
		StringIntMap::iterator found = mStringInt.find(index);
		if ( found != mStringInt.end() )
		{
			assert(0);
		}
		else
		{
			mStringInt[index] = IntEntry(v);
			mIntStringMap[v] = str;
		}
	}

	gpstxt::GtU32 GetSize(void) const
	{
		return (gpstxt::GtU32)mStringInt.size();
	}


	void getStringUsage(StringUsageVector &result)
	{
		for (StringIntMap::iterator i=mStringInt.begin(); i!=mStringInt.end(); ++i)
		{
			StringUsage su;
			su.mString = (const char *)(*i).first;
			su.mValue = (*i).second.mValue;
			su.mCount = (*i).second.mCount;
			result.push_back(su);
		}
		std::sort( result.begin(), result.end() );
	}

	const char * getString(gpstxt::GtU32 v)
	{
		const char *ret = NULL;
		IntStringMap::iterator found = mIntStringMap.find(v);
		if ( found != mIntStringMap.end() )
		{
			ret = (*found).second;
		}
		return ret;
	}

	void setCaseSensitive(bool state)
	{
		mStringTable.setCaseSensitive(state);
	}

	void release(void)
	{
		mStringTable.release();
		mStringInt.clear();
		mIntStringMap.clear();
	}

private:
	StringTable		mStringTable;
	StringIntMap	mStringInt;
	IntStringMap	mIntStringMap;
};

#pragma warning(pop)

#endif
